package com.cskaoyan.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.cskaoyan.mall.commons.constant.SysRetCodeConstants;
import com.cskaoyan.mall.commons.exception.ValidateException;
import com.cskaoyan.mall.commons.util.CookieUtil;
import com.cskaoyan.user.constants.UserRetCode;
import com.cskaoyan.user.dal.entitys.Member;
import com.cskaoyan.user.dal.entitys.UserVerify;
import com.cskaoyan.user.dal.persistence.MemberMapper;
import com.cskaoyan.user.dal.persistence.UserVerifyMapper;
import com.cskaoyan.user.dto.KaptchaCodeRequest;
import com.cskaoyan.user.dto.KaptchaCodeResponse;
import com.cskaoyan.user.dto.UserRegisterRequest;
import com.cskaoyan.user.dto.UserRegisterResponse;
import com.cskaoyan.user.service.IKaptchaService;
import com.cskaoyan.user.service.UserRegisterService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import tk.mybatis.mapper.entity.Example;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * @author sosog
 * @version 1.0.0
 * @description TODO
 * @date 2022/6/17 21:19
 */
@Slf4j
@Service
public class UserRegisterServiceImpl implements UserRegisterService {
    @Autowired
    JavaMailSender sender;

    @Value("${email.text}")
    private String emailUrl;

    @Value("${spring.mail.username}")
    private String host;

    @Autowired
    private IKaptchaService iKaptchaService;

    @Autowired
    private MemberMapper memberMapper;

    @Autowired
    private UserVerifyMapper userVerifyMapper;

    @Override
    public UserRegisterResponse register(UserRegisterRequest request, HttpServletRequest servletRequest) {
        UserRegisterResponse response = new UserRegisterResponse();
        // 1.验证传来的验证码
        String captcha = request.getCaptcha();
        if (!checkCaptcha(captcha, servletRequest)) {
            log.info("验证码错误");
            response.setCode(UserRetCode.KAPTCHA_CODE_ERROR.getCode());
            response.setMsg(UserRetCode.KAPTCHA_CODE_ERROR.getMessage());
            return response;
        }
        // 用户信息判空
        request.requestCheck();
        // 用户名重复判断
        validateUserName(request.getUserName());

        // 邮箱重复判断
        validateEmail(request.getEmail());

        // 2.向用户表中插入一条记录
        Member member = new Member();
        member.setUsername(request.getUserName());
        member.setEmail(request.getEmail());
        // 密码加密，使用md5
        String pwd = DigestUtils.md5DigestAsHex(request.getUserPwd().getBytes());
        member.setPassword(pwd);
        // 设置注册时间和更新时间
        member.setCreated(new Date());
        member.setUpdated(new Date());
        // 设置是否激活【其实可以不设置，数据库中有默认值是N】
        member.setIsVerified("N");
        // 设置用户的状态
        member.setState(1);
        int affectedRows = memberMapper.insertSelective(member);
        if (affectedRows != 1) {
            log.info("注册失败");
            response.setCode(UserRetCode.USER_REGISTER_FAILED.getCode());
            response.setMsg(UserRetCode.USER_REGISTER_FAILED.getMessage());
            return response;
        }

        // 3.向用户验证表中插入一条记录
        UserVerify userVerify = new UserVerify();
        userVerify.setUsername(request.getUserName());

        // 生成一个不重复的uuid
        String key = member.getUsername() + member.getPassword() + UUID.randomUUID();
        String uuid = DigestUtils.md5DigestAsHex(key.getBytes());
        userVerify.setUuid(uuid);
        userVerify.setRegisterDate(new Date());
        userVerify.setIsVerify("N");
        userVerify.setIsExpire("N");

        int rows = userVerifyMapper.insertSelective(userVerify);
        if (rows != 1) {
            log.info("用户注册失败插入验证数据失败");
            response.setCode(UserRetCode.USER_REGISTER_VERIFY_FAILED.getCode());
            response.setMsg(UserRetCode.USER_REGISTER_VERIFY_FAILED.getMessage());
            return response;
        }
        // 4.发送用户激活邮件【创建一个子线程来异步发送邮件，避免占用服务运行时间而导致服务超时而失败】
        // 学习了消息中间件后可以使用消息中间件来实现邮件的异步发送
        Thread thread = new Thread(() -> {
            sendEmail(request.getEmail(), request.getUserName(), uuid);
        });
        thread.start();
        // 打印成功日志
        log.info("注册成功，注册参数request:{}", JSON.toJSONString(request));
        response.setCode(UserRetCode.SUCCESS.getCode());
        response.setMsg(UserRetCode.SUCCESS.getMessage());
        return response;
    }

    /**
     * 验证邮箱是否重复
     * @param email
     */
    private void validateEmail(String email) {
        Example example = new Example(Member.class);
        example.createCriteria()
                .andEqualTo("email", email)
                .andEqualTo("isVerified", "Y");
        List<Member> members = memberMapper.selectByExample(example);
        if (!CollectionUtils.isEmpty(members)) {
            throw new ValidateException(UserRetCode.MAIL_ALREADY_USED.getCode(), UserRetCode.MAIL_ALREADY_USED.getMessage());
        }
    }

    /**
     * 验证用户名是否重复
     * @param userName
     */
    private void validateUserName(String userName) {
        // 通过userName去查用户表和用户验证表来判断是否有重复
        Example example = new Example(Member.class);
        example.createCriteria().andEqualTo("username", userName);
        List<Member> members = memberMapper.selectByExample(example);
        // 如果查询结果不为空需要抛出异常并给出相应的提示
        if (!CollectionUtils.isEmpty(members)) {
            String isVerified = members.get(0).getIsVerified();
            // 查询结果已经验证，说明用户已经注册且验证完成，提醒用户直接登录
            if (isVerified.equals("Y")) {
                throw new ValidateException(UserRetCode.USERNAME_ALREADY_EXISTS.getCode(), UserRetCode.USERNAME_ALREADY_EXISTS.getMessage());
            }
            // 查询结果未验证，说明用户注册但未验证，提醒用户去验证
            throw new ValidateException(UserRetCode.USER_ISVERFIED_ERROR.getCode(), UserRetCode.USER_ISVERFIED_ERROR.getMessage());
        }
    }

    /**
     * 检测验证码
     * @param captcha
     * @param servletRequest
     * @return
     */
    private boolean checkCaptcha(String captcha, HttpServletRequest servletRequest) {
        String kaptcha_uuid = CookieUtil.getCookieValue(servletRequest, "kaptcha_uuid");
        KaptchaCodeRequest kaptchaCodeRequest = new KaptchaCodeRequest();
        kaptchaCodeRequest.setCode(captcha);
        kaptchaCodeRequest.setUuid(kaptcha_uuid);
        KaptchaCodeResponse response = iKaptchaService.validateKaptchaCode(kaptchaCodeRequest);
        String code = response.getCode();
        return code.equals(SysRetCodeConstants.SUCCESS.getCode());
    }

    /**
     * 发送邮件
     * @param email
     * @param username
     * @param uuid
     */
    private void sendEmail(String email,String username, String uuid) {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setSubject("DuoLaiMall用户激活");
        mailMessage.setFrom(host);
        mailMessage.setTo(email);
        String text = String.format(emailUrl, username, uuid);
        mailMessage.setText(text);
        try {
            sender.send(mailMessage);
        } catch (MailException e) {
            // 处理诸如用户输入错误邮箱之类的邮箱错误
            log.error(e.getMessage());
            throw new ValidateException(UserRetCode.MAIL_INVALID.getCode(), UserRetCode.MAIL_INVALID.getMessage());
        }
    }
}
